Choosing
Help with the Keyboard
An
application can enable the user to choose a help topic with the keyboard by
intercepting the F1 key. Intercepting this key lets the user select a
menu, menu item, dialog box, message box, or control window and view help for
it with a single keystroke.
To intercept
the F1
key, the application must install a message-filter procedure by using the SetWindowsHook function. This allows the
application to examine all keystrokes for the application, regardless of which
window has the input focus. If the filter procedure detects the F1 key, it posts
a WM_F1DOWN message (application-defined) to the application's main window
procedure. The procedure then determines which help topic to display.
The filter
procedure should have the following form:
int CALLBACK FilterFunc(nCode, wParam, lParam)
int nCode;
WORD wParam;
DWORD lParam;
{
LPMSG lpmsg = (LPMSG)lParam;
if ((nCode == MSGF_DIALOGBOX || nCode == MSGF_MENU) &&
lpmsg->message ==
WM_KEYDOWN && lpmsg->wParam == VK_F1) {
PostMessage(hWnd, WM_F1DOWN,
nCode, 0L);
}
DefHookProc(nCode, wParam, lParam,
&lpFilterFunc);
return 0;
}
The
application should install the filter procedure after creating the main window,
as shown in the following example:
lpProcInstance = MakeProcInstance(FilterFunc,
hInstance);
if (lpProcInstance == NULL)
return FALSE;
lpFilterFunc = SetWindowsHook(WH_MSGFILTER, lpProcInstance);
The filter
procedure sends a WM_F1DOWN message only when the F1 key is pressed
in a dialog box, message box, or menu. Many applications also display the
Contents topic if no menu, dialog box, or message box is selected when the user
presses the F1 key. In this case, the application should define the F1 key as an
accelerator key that starts Windows Help.
To create an
accelerator key, the application s resource-definition file must define an
accelerator table, as follows:
1 ACCELERATORS
BEGIN
VK_F1, IDM_HELP_CONTENTS, VIRTKEY
END
To support
the accelerator key, the application must load the accelerator table by using
the LoadAccelerators
In addition
to installing the filter procedure, the application must keep track of which
menu, menu item, dialog box, or message box is currently selected. In other
words, when the user selects an item, the application must set a global
variable indicating the current context. For dialog and message boxes, the
application should set the global variable immediately before calling the DialogBox
case WM_MENUSELECT:
/*
* Set dwCurrentHelpId to the help ID
of the menu item that is
* currently selected.
*/
if (HIWORD(lParam) == 0) /* no menu selected */
dwCurrentHelpId = ID_NONE;
else if
(lParam & MF_POPUP) { /*
pop-up selected */
if ((HMENU)wParam == hMenuFile)
dwCurrentHelpId = ID_FILE;
else if ((HMENU)wParam ==
hMenuEdit)
dwCurrentHelpId = ID_EDIT;
else if ((HMENU)wParam ==
hMenuHelp)
dwCurrentHelpId = ID_HELP;
else
dwCurrentHelpId = ID_SYSTEM;
}
else /* menu item
selected */
dwCurrentHelpId = wParam;
break;
In this
example, the hMenuFile, hMenuEdit, and hMenuHelp
parameters must previously have been set to specify the corresponding menu
handles. An application can use the GetMenu
When the main
window procedure finally receives a WM_F1DOWN message, it should use the
current value of the global variable to display a help topic. The application
can also provide help for individual controls in a dialog box by determining
which control has the focus at this point, as shown in the following example:
case WM_F1DOWN:
/*
* If there is a current help
context, display it.
*/
if (dwCurrentHelpId != ID_NONE) {
DWORD dwHelp = dwCurrentHelpId;
/*
* Check for context-sensitive
help for individual dialog
* box controls.
*/
if (wParam == MSGF_DIALOGBOX) {
WORD wID =
GetWindowWord(GetFocus(), GWW_ID);
if (wID != IDOK &&
wID != IDCANCEL)
dwHelp = (DWORD) wID;
}
WinHelp(hWnd, szHelpFileName, HELP_CONTEXT, dwHelp);
/*
* This call is used to remove
the highlighting from the
* System menu, if necessary.
*/
DrawMenuBar(hWnd);
}
break;
When the
application ends, it must remove the filter procedure by using the UnhookWindowsHook